<script setup lang="ts">
import { chatLogAdd, chatLogGet, stopChat } from '@/services/chat';
import { model, modelId, modelType } from '@/utils/request';
import { getStorage, saveStorage } from '@/utils';
import Dot from '@/components/dot.vue';
import { fetchEventSource } from '@microsoft/fetch-event-source';
import { reactive, onMounted, watch, nextTick } from 'vue';
import { ElMessage } from 'element-plus';
import _ from 'lodash';
import { marked } from 'marked';
import copy from '@/assets/chat/copy.svg';
import refresh from '@/assets/chat/refresh.svg';
import stopicon from '@/assets/chat/stop.svg';
import stop_active from '@/assets/chat/stop_active.svg';
// 教案设计
import chatImg from '@/assets/ai/aiAvatar.png';
import MessageItem from './messageItem.vue';
// ai 出题
// import chatImg from "@/assets/ai/aiCt.png";

const props = defineProps({
  question: String,
  selectId: String,
  list: Array,
  exts: Object as any, // 配置
  hearingRoleList: Array as any, // 听力角色列表
});

const user = { id: getStorage('userId'), nickname: getStorage('nickname') || '' };
const botId: any = getStorage('botId');

const state = reactive({
  question: '',
  questionMax: 2400,
  loading: false,
  answer: '',
  // 右侧对话记录
  chats: [] as any,
  chatsToday: [] as any,
  chatsYesterday: [] as any,
  chatsEarly: [] as any,
  // 左侧对话记录
  chatLogs: [] as any,
  chatId: '',
  modelTypes: [] as any,
  modelType,
  model,
  modelId,
  modelTypeTemp: [] as any,
  drawerOpen: false,
  refreshIndex: 0,
  refreshId: '',
  refreshItem: {} as any,
  showBtnId: '',
  stopbtnicon: stopicon,
  // 对话记录列表
  // 新ai, diffy相关
  conversation_id: '',
  task_id: '',
  // 上传文件
  loadingInstance: null as any,
  audioPath: '',
  // 输入文件url
  linkUrl: '',
  linkUrlTemp: '',
  linkList: [
    // {
    //   type: 'document',
    //   transfer_method: 'remote_url',
    //   url: 'https://huashujiaoyu.com/static/files/202412261730302345.docx',
    // },
  ] as any,
  // 文件类型
  wolds: 'txt,doc,docx,pdf,ppt,pptx,xlsx,xls',
  imgs: 'jpg,jpeg,png,gif,webp,svg',
  audios: 'mp3,m4a,wav,webm,amr',
  videos: 'mp4,mov,mpeg,mpga',
});

console.log('接受', props.hearingRoleList);

const setBntId = (e: any, id?: any) => {
  e.stopPropagation();
  state.showBtnId = id;
};
const clearBtnId = () => {
  state.showBtnId = '';
};

const getChatLog = async () => {
  if (state.chatId) {
    const { errCode, data }: any = await chatLogGet({
      chatId: state.chatId,
    });
    if (errCode === 0) {
      state.chatLogs = _.map(data, v => ({
        ...v,
        files: JSON.parse(v.filePath || '[]'),
      }));
      nextTick(() => {
        scrollToBottom();
      });
    }
  } else {
    state.chatLogs = [];
  }
};

const emits = defineEmits(['aiAnswerOrer', 'roleConfirm']);

const onEnded = async () => {
  state.loading = false;
  if (!state.chatLogs[state.chatLogs.length - 1].answer) {
    // 没有返回内容
    state.chatLogs[state.chatLogs.length - 1].answer = '检测到内容包含敏感词，说点别的吧~';
  }
  // 考虑存入files
  const { errCode, data }: any = await chatLogAdd({
    botId,
    chatId: state.chatId,
    modelType: state.modelType,
    question: state.chatLogs[state.chatLogs.length - 1].question,
    answer: state.chatLogs[state.chatLogs.length - 1].answer,
    conversation_id: state.conversation_id,
    filePath: JSON.stringify(state.chatLogs[state.chatLogs.length - 1].files),
  });
  if (errCode === 0) {
    state.chatLogs[state.chatLogs.length - 1].id = data.id;
    state.chatId = data.chatId;
    // 回答结束了
    setTimeout(() => emits('aiAnswerOrer', data.chatId), 500);
  }
};

const onEndedrefresh = async () => {
  state.loading = false;
  if (!state.chatLogs[state.refreshIndex].answer) {
    // 没有返回内容
    state.chatLogs[state.refreshIndex].answer = '检测到内容包含敏感词，说点别的吧~';
  }
  await chatLogAdd({
    botId,
    id: state.refreshId,
    chatId: state.chatId,
    modelType: state.modelType,
    question: state.chatLogs[state.refreshIndex].question,
    answer: state.chatLogs[state.refreshIndex].answer,
    conversation_id: state.conversation_id,
  });
};
const chatQuestion = (question: string) => {
  state.question = question;
  chatStart();
};
const chatStart = async () => {
  if (state.loading) {
    ElMessage.error('请等待生成结果');
    return;
  }
  if (!state.question) {
    ElMessage.error('请输入内容');
    return;
  }
  state.loading = true;
  // 新的请求方法
  // import.meta.env.VITE_TYPE，获取配置的api,上线的时候要取这个
  // conversation_id,是说明请求的是同一条记录
  let args = {
    botId,
    // inputs: { modelType: state.model },
    query: state.question,
    // response_mode: 'streaming',
    // conversation_id: state.conversation_id,
    user: user.id,
  } as any;
  if (state.conversation_id) {
    args = { ...args, conversation_id: state.conversation_id };
  }
  if (!_.isEmpty(state.linkList)) {
    args = { ...args, files: state.linkList };
  }
  state.chatLogs.push({
    question: state.question,
    files: state.linkList,
    answer: '',
  });
  state.question = '';
  state.linkList = [];
  chatSendContent(args);
};

const chatSendContent = async (args: any) => {
  scrollToBottom();
  // const api = import.meta.env.VITE_TYPE === 'online' ? 'https://ai.huazhiqing.com/api' : 'http://192.168.1.98/api';
  const api = 'http://192.168.1.98/api';

  // 动态设置请求的接口
  fetchEventSource(`${api}/szjc-ai/dify/chat-stream`, {
    headers: {
      // Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    method: 'post',
    body: JSON.stringify({ ...args }),
    openWhenHidden: true,
    onmessage: e => {
      let { type, content, conversation_id, task_id } = JSON.parse(e.data) || {};
      if (type === 'message') {
        state.conversation_id = conversation_id;
        state.task_id = task_id;
        // // 格式1处理
        // if (content.indexOf('> 💭') !== -1) {
        //   content = content.replace('>', '');
        //   state.chatLogs[state.chatLogs.length - 1].answer += '<div class="dify-res1">';
        // }
        // if (content === '\n\n\n\n') {
        //   state.chatLogs[state.chatLogs.length - 1].answer += '</div>';
        // }
        // if (content.indexOf('\n>') !== -1) {
        //   content = content.replace('\n>', '\n');
        // }
        // // 格式2处理
        // if (content.indexOf('<think>') !== -1) {
        //   state.chatLogs[state.chatLogs.length - 1].answer += '<div class="">Thinking</div>';
        //   state.chatLogs[state.chatLogs.length - 1].answer += '<div class="dify-res1">';
        // }
        // if (content.indexOf('</think>') !== -1) {
        //   state.chatLogs[state.chatLogs.length - 1].answer += '</div>';
        // }
        state.chatLogs[state.chatLogs.length - 1].answer += content;
        scrollToBottom();
      }
      if (type === 'end') {
        // 结束了, 这个可以添加log记录
        state.linkUrlTemp = '';
        state.audioPath = '';
        onEnded();
      }
    },
    onerror: err => {
      // eventSource.close();
      throw err;
    },
    onclose: () => {
      state.question = '';
      state.loading = false;
    },
  });
};

const refreshlog = async (item: any, indexitem: any) => {
  clearBtnId();
  state.refreshIndex = indexitem;
  state.refreshId = item.id;
  state.loading = true;
  item.answer = '';
  // 新的请求方法
  // import.meta.env.VITE_TYPE，获取配置的api,上线的时候要取这个
  // conversation_id,是说明请求的是同一条记录
  let args = {
    botId,
    // inputs: { modelType: state.model },
    query: item.question,
    user: user.id,
  } as any;
  if (state.conversation_id) {
    args = { ...args, conversation_id: state.conversation_id };
  }
  const files = JSON.parse(item.filePath || '[]');
  if (files.length > 0) {
    args = { ...args, files };
  }
  // const api = import.meta.env.VITE_TYPE === 'online' ? 'https://ai.huazhiqing.com/api' : 'http://192.168.1.98/api';
  const api = 'http://192.168.1.98/api';
  fetchEventSource(`${api}/szjc-ai/dify/chat-stream`, {
    headers: {
      // Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    method: 'post',
    body: JSON.stringify({ ...args }),
    openWhenHidden: true,
    onmessage: e => {
      let { type, content, conversation_id, task_id } = JSON.parse(e.data) || {};
      if (type === 'message') {
        state.conversation_id = conversation_id;
        state.task_id = task_id;
        // // 格式1处理
        // if (content.indexOf('> 💭') !== -1) {
        //   content = content.replace('>', '');
        //   state.chatLogs[state.refreshIndex].answer += '<div class="dify-res1">';
        // }
        // if (content === '\n\n\n\n') {
        //   state.chatLogs[state.refreshIndex].answer += '</div>';
        // }
        // if (content.indexOf('\n>') !== -1) {
        //   content = content.replace('\n>', '\n');
        // }
        // 格式2处理
        state.chatLogs[state.refreshIndex].answer += content;
        scrollToBottom();
      }
      if (type === 'end') {
        // 结束了, 这个可以添加log记录
        onEndedrefresh();
      }
    },
    onerror: err => {
      // eventSource.close();
      // 抛出错误，就会停止发送信息
      // throw err;
    },
    onclose: () => {
      state.question = '';
      state.loading = false;
    },
  });
};

// 暂停
const chatEnd = async () => {
  // if (1) return;
  await stopChat({ taskId: state.task_id, user: user.id, botId });
  state.loading = false;
  setTimeout(() => {
    state.chatLogs[state.chatLogs.length - 1].answer += '\n[已停止生成]';
  }, 500);
};

// 新格式
const tomarked = (text: string) => {
  return marked ? marked(text) : text;
};

// tab切换 或者第一次赋值
watch(
  () => state.modelType,
  async () => {},
  { immediate: true },
);

watch(
  () => props.question,
  (newVal: any) => {
    state.question = newVal;
  },
);

const getRightLogs = (id: string) => {
  state.chatId = id;
  getChatLog();
};

watch(
  () => props.selectId,
  (newVal: any) => {
    getRightLogs(newVal);
  },
  {
    immediate: true,
    deep: true,
  },
);
watch(
  () => props.list,
  (newVal: any) => {
    state.chatLogs = newVal;
  },
  {
    immediate: true,
    deep: true,
  },
);

const newSpeak = async (next: Function) => {
  if (state.loading) {
    ElMessage.error('请等待生成结果');
    return;
  }
  state.chatId = '';
  state.question = '';
  state.chatLogs = [];
  state.conversation_id = '';
  next(0);
};

onMounted(async () => {
  const query = getStorage('query');
  if (!query) return;
  saveStorage('query', '');
  state.chatLogs.push({
    question: query,
    files: state.linkList,
    answer: '',
  });
  if (state.loading) {
    return;
  }
  state.loading = true;
  const args = {
    query: query,
    user: user.id,
    botId,
  };
  await chatSendContent(args);
});

const copytext = (item: any) => {
  const { answer } = item;
  const textareaC = document.createElement('textarea');
  textareaC.setAttribute('readonly', 'readonly'); //设置只读属性防止手机上弹出软键盘
  textareaC.value = answer;
  document.body.appendChild(textareaC); //将textarea添加为body子元素
  textareaC.select();
  const res = document.execCommand('copy');
  document.body.removeChild(textareaC); //移除DOM元素
  // 提示成功
  if (res) {
    ElMessage.success('复制成功');
  } else {
    ElMessage.error('复制失败');
  }
  clearBtnId();
};

const scrollToBottom = () => {
  const scrollDom = document.getElementById('scrollDom');
  const scrollDom2 = document.getElementById('scrollDom2');
  if (scrollDom) {
    scrollDom.scrollIntoView();
    setTimeout(() => {
      scrollDom2?.scrollIntoView();
    }, 0);
  }
};

const changeBorderColor = (type: any) => {
  const textarea = document.querySelector('.shuru_kuang');
  if (textarea) {
    if (type === 0) {
      textarea.setAttribute('style', 'border: 1px solid #dee8f9 !important;');
    } else {
      setTimeout(() => {
        scrollToBottom();
      }, 100);
      textarea.setAttribute('style', 'border: 1px solid #3C7EFF !important;');
    }
  }
};

const keydownHandle = (e: any, question: string) => {
  state.question = question;
  chatStart();
  // //  shift+enter 换行
  // if (e.keyCode === 13 && e.shiftKey) {
  //   state.question += '\n';
  // } else if (e.keyCode === 13) {
  //   _.debounce(chatStart, 50)();
  // }
};

// 下载文件
const fileDown = (url: any) => {
  // http://192.168.1.98/api/aigc/common/ossfile?filePath=aigc_team/file/9/9f/99f2518b049a6fdc54eb9b5fb4b754f6.docx
  const audioUrl = url.replace('https://ai.henanyuexue.com', 'http://192.168.1.159'); // 文件的 URL
  const link = document.createElement('a');
  link.href = audioUrl;
  link.download = '';
  link.click();
};

// 角色列表
const roleConfirm = (list: any) => {
  emits('roleConfirm', list);
};

defineExpose({
  changeBorderColor,
  keydownHandle,
  newSpeak,
  chatStart,
  chatQuestion,
});
</script>

<template>
  <div class="cheat_out" @click="clearBtnId">
    <div class="mid">
      <div :class="['mid_talk_list', state.linkList.length === 0 && 'mid_talk_list-h1']">
        <el-scrollbar v-if="state.chatLogs.length">
          <div v-for="(item, index) in state.chatLogs" :key="index" class="one_talk">
            <div v-if="item.question" class="user_talk">
              <div class="user_talk_kuang">
                <div v-if="(item.files || []).length > 0" class="">
                  <div v-for="(ite1, key) in item.files" :key="key">
                    <div v-if="ite1.url" class="file-s-l">
                      <span class="file-s-url">{{ ite1.url }}</span>
                      <el-icon class="down-icon" @click="fileDown(ite1.url)">
                        <Download />
                      </el-icon>
                    </div>
                  </div>
                </div>
                <pre class="user_talk_font">{{ item.question }}</pre>
              </div>
              <div class="user_avatar">{{ user.nickname.slice(0, 1) }}</div>
            </div>
            <div
              class="gpt_talk"
              @mouseenter="
                e => {
                  setBntId(e, item.id);
                }
              "
              @mouseleave="clearBtnId"
              @click="
                e => {
                  setBntId(e, item.id);
                }
              "
            >
              <div class="gpt_avatar">
                <el-image :src="chatImg" />
              </div>
              <div class="gpt_talk_kuang">
                <div class="gpt_talk_font">
                  <div>
                    <!-- <span>
                      <MessageItem :type="props.exts.botFlag" :answer="item.answer" @roleConfirm="roleConfirm" />
                    </span> -->
                  </div>
                  <Dot v-if="state.loading && index === state.chatLogs.length - 1" />
                </div>

                <div class="gn_btn_out gn_btn_out_pc">
                  <div
                    :class="index === state.chatLogs.length - 1 ? 'btn_group' : 'btn_group left_move'"
                    v-if="item.id === state.showBtnId && !state.loading"
                  >
                    <div
                      class="icon_out mgright"
                      v-if="index === state.chatLogs.length - 1"
                      @click="refreshlog(item, index)"
                    >
                      <el-popover :width="74" popper-class="refresh-pop" trigger="hover" placement="top">
                        <div class="reset-sc">重新生成</div>
                        <template #reference>
                          <el-image :src="refresh" />
                        </template>
                      </el-popover>
                    </div>
                    <div class="icon_out" @click="copytext(item)">
                      <el-popover :width="74" popper-class="refresh-pop" trigger="hover" placement="top">
                        <div class="reset-sc">复制</div>
                        <template #reference>
                          <el-image :src="copy" />
                        </template>
                      </el-popover>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div id="scrollDom2" class="scrollDomclass2"></div>
          <div id="scrollDom" class="scrollDomclass"></div>
        </el-scrollbar>
        <div class="welcome_out" v-else>
          <div class="welcome_font">欢迎您的到来！ 很高兴在数字教材对话与您见面</div>
          <div class="welcome_font_small">现在直接在下方输入内容对话吧</div>
        </div>
      </div>
      <div class="mid_bottom">
        <div
          v-if="state.loading"
          class="stop_btn"
          @mouseenter="
            () => {
              state.stopbtnicon = stop_active;
            }
          "
          @mouseleave="
            () => {
              state.stopbtnicon = stopicon;
            }
          "
          @click="chatEnd"
        >
          <el-image class="stop_icon" :src="state.stopbtnicon"></el-image>
          生成中
        </div>
        <slot name="input"></slot>
      </div>
    </div>
  </div>
</template>

<style lang="scss">
.cheat_out {
  .el-select .el-input__wrapper {
    background: rgba(60, 126, 255, 0.05) !important;
    border: 0px solid #dee8f9 !important;
    height: 32px !important;
    box-sizing: border-box;

    .el-input__inner {
      color: #3c7eff !important;
      height: 32px !important;
    }
  }

  .el-select .el-input .el-select__caret {
    color: #3c7eff !important;
  }

  .el-textarea__inner {
    color: #375275 !important;
  }

  input {
    // 解决输入框第一次点击闪烁问题
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0) !important;
  }

  .el-select:hover .el-input__wrapper {
    border: 1px solid #3c7eff !important;
    box-shadow: none !important;
    box-sizing: border-box;
    padding: 1px 14px;
  }

  .el-select .el-input__wrapper.is-focus {
    // border: 1px solid #dee8f9 !important;
    box-shadow: none !important;
  }
}

.refresh-pop {
  .el-popper__arrow::before {
    background-color: rgba(60, 126, 255, 0.05) !important;
  }
}

.refresh-pop {
  padding: 8px !important;
  background: #3c7eff !important;
  min-width: 50px !important;

  .el-popper__arrow::before {
    background-color: #3c7eff !important;
  }

  .reset-sc {
    color: #ffffff;
    font-size: 14px;
    text-align: center;
  }
}

// 新的dify
.dify-res1 {
  background: rgb(248, 248, 248);
  color: #808080;
  padding: 8px;
  border-radius: 4px;
}

.file-con {
  text-align: center;

  .link-input {
    width: 240px;

    .el-input-group__append {
      padding: 0 8px;
      cursor: pointer;
    }
  }

  .el-divider--horizontal {
    margin: 14px 0;
  }

  .upload-demo {
    text-align: center;

    .file-btn {
      width: 240px;
    }
  }
}
</style>
<style lang="scss" scoped>
@use './index.scss';
</style>
